package manage

import (
	"encoding/binary"
	"fmt"

	"github.com/golang/protobuf/proto"
	"hundsun.com/hsl/hschain/common/log"
	"hundsun.com/hsl/hschain/common/types"
	"hundsun.com/hsl/hschain/common/util/pb"
	"hundsun.com/hsl/hschain/contract/native/manage/common"
	execcomm "hundsun.com/hsl/hschain/executor/common"
	"hundsun.com/hsl/hschain/protos/manage"
)

// Query 查询manage合约
func (nm *nativeManage) Query(ctx *execcomm.ExecContent, funcName string, param []byte) (proto.Message, error) {
	log.Infof("receive native manage query request, funcName: %s\n", funcName)
	switch funcName {
	case common.QueryManageFlowStatus:
		return nm.queryManageFlowStatus(ctx, param)
	case common.QueryUserGroup:
		return nm.queryUserGroup(ctx, param)
	case common.QueryCreatedManageFlow:
		return nm.queryCreatedManageFlow(ctx, param)
	case common.QueryVotedManageFlow:
		return nm.queryVotedManageFlow(ctx, param)
	case common.QueryNeedVotedManageFlow:
		return nm.queryNeedVotedManageFlow(ctx, param)
	case common.QueryManageFlowCfg:
		return nm.queryManageFlowCfg(ctx, param)
	case common.QueryCreatedManageFlowByAddr:
		return nm.queryCreatedManageFlowByAddr(ctx, param)
	case common.QueryVoteHistoryByFlowID:
		return nm.queryVoteHistoryByFlowID(ctx, param)
	default:
		log.Error("unknown native manage query func name")
		return nil, fmt.Errorf("%s not implemented", funcName)
	}
}

func (nm *nativeManage) queryManageFlowStatus(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	queryParam := &manage.ManageFlowQueryFlowParam{}
	if len(param) == 0 {
		queryParam.FlowID = 0
	} else {
		err := proto.Unmarshal(param, queryParam)
		if err != nil {
			log.Errorf("unmarshal query param failed, err: %s", err.Error())
			return nil, err
		}
	}

	var status = &manage.ManageFlowStatus{}
	statusKey := common.GenStatusKey(queryParam.FlowID)
	statusInfo, err := ctx.Get(statusKey)
	if err != nil {
		log.Errorf("get flow status info failed, err: %s", err.Error())
		return nil, err
	}
	err = proto.Unmarshal(statusInfo, status)
	if err != nil {
		log.Errorf("unmarshal flow status info failed, err: %s", err.Error())
		return nil, err
	}
	return status, nil
}

func (nm *nativeManage) queryManageFlowCfg(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	queryParam := &manage.ManageFlowQueryCfgParam{}
	if len(param) == 0 {
		queryParam.Name = ""
	} else {
		err := proto.Unmarshal(param, queryParam)
		if err != nil {
			log.Errorf("unmarshal query param failed, err: %s", err.Error())
			return nil, err
		}
	}

	var cfg = &manage.ManageFlowCfg{}
	flowCfgKey := common.GenCfgKey(queryParam.Name)
	cfgInfo, err := ctx.Get(flowCfgKey)
	if err != nil {
		log.Errorf("get flow cfg info failed, err: %s", err.Error())
		return nil, err
	}
	err = proto.Unmarshal(cfgInfo, cfg)
	if err != nil {
		log.Errorf("unmarshal flow status info failed, err: %s", err.Error())
		return nil, err
	}
	return cfg, nil
}

func (nm *nativeManage) queryUserGroup(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	var reqParam = &manage.ManageFlowQueryUserGroup{}
	err := proto.Unmarshal(param, reqParam)
	if err != nil {
		return nil, err
	}

	var validator = &manage.ManageFlowValidatorInfo{}
	groupKey := common.GenGroupInfoKey(reqParam.GroupType)
	validatorInfo, _ := ctx.Get(groupKey)
	if len(validatorInfo) > 0 {
		err = proto.Unmarshal(validatorInfo, validator)
		if err != nil {
			log.Errorf("unmarshal flow status info failed, err: %s", err.Error())
			return nil, err
		}
		return validator, nil
	} else if reqParam.GroupType == manage.UserGroupType_MainBoard {
		genesisTransactionPayload := pb.GetGenesisTransactionPayload()
		if genesisTransactionPayload != nil && genesisTransactionPayload.Manager != nil &&
			len(genesisTransactionPayload.Manager.Addrs) > 0 {
			validator.Validators = append(validator.Validators, genesisTransactionPayload.Manager.Addrs...)
			return validator, nil
		}
	}
	return nil, types.ErrNotFound
}

// queryCreatedManageFlow 查询已创建管理流程
func (nm *nativeManage) queryCreatedManageFlow(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	if len(param) == 0 {
		log.Error("param is nil")
		return nil, types.ErrInvalidParam
	}
	queryParam := &manage.ManageFlowCreateFlowParam{}
	err := proto.Unmarshal(param, queryParam)
	if err != nil {
		log.Errorf("unmarshal query param failed, err: %s", err.Error())
		return nil, err
	}

	flowIDKey := common.GenIDKey()
	flowIDBytes, err := ctx.Get(flowIDKey)
	if err != nil {
		log.Errorf("get flow id key failed, err: %s", err.Error())
		return nil, err
	}

	currentFlowID := binary.BigEndian.Uint32(flowIDBytes)

	var resp = &manage.ManageFlowCreateFlowResp{}
	var id uint32
	for id = 0; id <= currentFlowID; id++ {
		flowStatusKey := common.GenStatusKey(id)
		flowBytes, err := ctx.Get(flowStatusKey)
		if err != nil {
			log.Errorf("get manage flow status by key %s failed, err: %s", flowStatusKey, err.Error())
			return nil, err
		}
		var flow = &manage.ManageFlowStatus{}
		proto.Unmarshal(flowBytes, flow)
		resp.Flows = append(resp.Flows, flow)
	}
	log.Infof("query created manage flow successfully, got %d resp", len(resp.Flows))
	return resp, nil
}

// queryCreatedManageFlow 根据地址查询已创建管理流程
func (nm *nativeManage) queryCreatedManageFlowByAddr(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	if len(param) == 0 {
		log.Error("param is nil")
		return nil, types.ErrInvalidParam
	}
	queryParam := &manage.ManageFlowQueryCreateFlow{}
	err := proto.Unmarshal(param, queryParam)
	if err != nil {
		log.Errorf("unmarshal query param failed, err: %s", err.Error())
		return nil, err
	}
	flowIDKey := common.GenIDKey()
	flowIDBytes, err := ctx.Get(flowIDKey)
	if err != nil {
		return nil, err
	}
	flowID := binary.BigEndian.Uint32(flowIDBytes)
	var resp = &manage.ManageFlowCreateFlowResp{}
	for id := queryParam.Id; id <= flowID; id++ {
		flowStatusKey := common.GenStatusKey(id)
		flowBytes, err := ctx.Get(flowStatusKey)
		if err != nil {
			log.Errorf("get manage flow status by key %s failed, err: %s", flowStatusKey, err.Error())
			return nil, err
		}
		var flow = &manage.ManageFlowStatus{}
		proto.Unmarshal(flowBytes, flow)
		if flow.Creator == queryParam.Address {
			resp.Flows = append(resp.Flows, flow)
		}
	}
	log.Infof("query created manage flow successfully, got %d resp", len(resp.Flows))
	return resp, nil
}

// queryVoteHistoryByFlowID
func (nm *nativeManage) queryVoteHistoryByFlowID(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	var queryParam = &manage.ManageFlowVoteParam{}
	if len(param) == 0 {
		queryParam.FlowID = 0
	} else {
		err := proto.Unmarshal(param, queryParam)
		if err != nil {
			log.Errorf("unmarshal query param failed, err: %s", err.Error())
			return nil, err
		}
	}

	var resp = &manage.ManageFlowVoteHistoryResp{}
	historyKey := common.GenVoteHistoryKey(queryParam.FlowID)
	validatorBytes, err := ctx.Get(historyKey)
	if err != nil && err != types.ErrNotFound {
		log.Errorf("get validator info from db failed, err: %s", err.Error())
		return nil, err
	}

	var validatorInfo = &manage.ManageFlowValidatorInfo{}
	if len(validatorBytes) > 0 {
		err = proto.Unmarshal(validatorBytes, validatorInfo)
		if err != nil {
			log.Errorf("unmarshal validator info failed, err: %s", err.Error())
			return nil, err
		}
		resp.VotedAddress = append(resp.VotedAddress, validatorInfo.Validators...)
	}
	return resp, nil
}

// queryVotedManageFlow 查询已投票的管理流程
func (nm *nativeManage) queryVotedManageFlow(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	return nil, nil
}

// queryNeedVotedManageFlow 查询需要投票的管理流程
func (nm *nativeManage) queryNeedVotedManageFlow(ctx *execcomm.ExecContent, param []byte) (proto.Message, error) {
	return nil, nil
}
